package com.bolt.support.spring.jpa.query;

import com.bolt.support.query.toolkit.DialectUtil;
import com.bolt.support.query.toolkit.JPAUtil;
import com.bolt.support.spring.jpa.transform.ConfigSqlTransformers;
import com.bolt.support.sqlconfig.base.SQLConfiguration;
import com.bolt.support.sqlconfig.base.SqlText;
import org.hibernate.query.internal.NativeQueryImpl;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.support.PageableExecutionUtils;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.math.BigInteger;
import java.util.List;

/**
 * Created by Administrator on 2017/8/17.
 */
public abstract class JdbcQueryExecution {

    protected final EntityManager entityManager;

    protected final JdbcQueryMethod method;

    protected final ParametersParameterAccessor accessor;

    protected final SQLConfiguration sqlConfiguration;


    public JdbcQueryExecution(EntityManager em, JdbcQueryMethod md, ParametersParameterAccessor accessor,SQLConfiguration sqlConfiguration) {
        this.entityManager = em;
        this.method = md;
        this.accessor = accessor;
        this.sqlConfiguration = sqlConfiguration;
    }

    protected void createRowMapper(Query query) {
        Class <?> returnType = method.getReturnedObjectType();
        query.unwrap(NativeQueryImpl.class).setResultTransformer(ConfigSqlTransformers.aliasToBean(returnType, this.sqlConfiguration));

    }


    public Object execute(SqlText sqlText) {

        Object result;
        try {
            result = doExecute(sqlText);
        } catch (IncorrectResultSizeDataAccessException ex) {
            return null;
        }
        if (result == null) {
            return null;
        }
        return result;
    }

    protected abstract Object doExecute(SqlText sqlText);

}

class CollectionExecution extends JdbcQueryExecution {

    public CollectionExecution(EntityManager em, JdbcQueryMethod md, ParametersParameterAccessor accessor,SQLConfiguration sqlConfiguration) {
        super(em, md, accessor,sqlConfiguration);
    }

    @Override
    protected Object doExecute(SqlText sqlText) {
        Query query = entityManager.createNativeQuery(sqlText.getSql());
        JPAUtil.setParameter(query, sqlText.getParameterValues());
        createRowMapper(query);
        return query.getResultList();
    }
}

class PagedExecution extends JdbcQueryExecution {

    public PagedExecution(EntityManager em, JdbcQueryMethod md, ParametersParameterAccessor accessor,SQLConfiguration sqlConfiguration) {
        super(em, md, accessor,sqlConfiguration);
    }


    @Override
    protected Object doExecute(SqlText sqlText) {
        Pageable pageable = accessor.getPageable();
        Query pageQuery = entityManager.createNativeQuery(sqlText.getSql());
        Query queryCount = entityManager.createNativeQuery(DialectUtil.getCountSql(sqlText.getSql()));

        JPAUtil.setParameter(pageQuery, sqlText.getParameterValues());

        JPAUtil.setParameter(queryCount, sqlText.getParameterValues());

        createRowMapper(pageQuery);
        if (pageable.isPaged()) {
            pageQuery.setFirstResult((int) pageable.getOffset());
            pageQuery.setMaxResults(pageable.getPageSize());
        }
        List <?> resultList = pageQuery.getResultList();
        return PageableExecutionUtils.getPage(resultList, accessor.getPageable(), () -> {
            return ((BigInteger) queryCount.getSingleResult()).longValue();
        });
    }
}

class SingleObjectExecution extends JdbcQueryExecution {

    public SingleObjectExecution(EntityManager em, JdbcQueryMethod md, ParametersParameterAccessor accessor,SQLConfiguration sqlConfiguration) {
        super(em, md, accessor,sqlConfiguration);
    }

    @Override
    protected Object doExecute(SqlText sqlText) {
        Query query = entityManager.createNativeQuery(sqlText.getSql());
        JPAUtil.setParameter(query, sqlText.getParameterValues());
        createRowMapper(query);
        return query.getSingleResult();
    }
}







